#include "Sender.h"
#include "IoCompletionPort.h"
#include "InternalTypes.h"
#include "types.h"


LONG SendIPCMessage(LPCTSTR szIPCName, LPVOID pvMsg, DWORD dwMsgSize, LPVOID pvResponse, DWORD dwResponseSize, DWORD dwTimeout) {
    if(!pvMsg || !pvResponse)
        return IPC_UNDEFINED_ERROR;

    TCHAR szPipeName[MAX_PATH + 1] = {0};
    _tcscpy_s(szPipeName, MAX_PATH, TEXT("\\\\.\\pipe\\"));
    _tcscat_s(szPipeName, MAX_PATH, szIPCName);

    HANDLE hPipe = INVALID_HANDLE_VALUE;
    DWORD dwGLE = 0;

    do {
        if ((hPipe = CreateFile(szPipeName,
                                GENERIC_READ |
                                GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                FILE_FLAG_OVERLAPPED,
                                NULL)) == INVALID_HANDLE_VALUE) {
            dwGLE = GetLastError();

            if (dwGLE == ERROR_PIPE_BUSY) {
                if (WaitNamedPipe(szPipeName, NMPWAIT_USE_DEFAULT_WAIT))
                    continue;
            }
        }

        break;
    }
    while (true);

    if(hPipe == INVALID_HANDLE_VALUE)
        return IPC_UNDEFINED_ERROR;

    DWORD dwMode = PIPE_READMODE_MESSAGE;

    if (!SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL)) {
        CloseHandle(hPipe);
        return IPC_UNDEFINED_ERROR;
    }

    HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if(!hEvent)
        return IPC_UNDEFINED_ERROR;

    OVERLAPPED olp;
    memset(&olp, 0, sizeof(olp));
    olp.hEvent = hEvent;

    MSGREQ_HEADER reqHeader;
    reqHeader.bNeedAnswer = TRUE;
    reqHeader.dwHeaderSize = sizeof(MSGREQ_HEADER);
    reqHeader.dwContentSize = dwMsgSize;

    DWORD dwReqSize = reqHeader.dwHeaderSize + reqHeader.dwContentSize;
    char *pReq = (char *)malloc(dwReqSize);

    if(!pReq) {
        SAFE_CLOSE(hEvent);
        return IPC_UNDEFINED_ERROR;
    }

    memcpy(pReq, &reqHeader, sizeof(MSGREQ_HEADER));
    memcpy((pReq + sizeof(MSGREQ_HEADER)), pvMsg, dwMsgSize);

    LONG lRet = 0;
    DWORD dwBytesRead = 0;
    DWORD dwLastErr = 0;

    if (!TransactNamedPipe(hPipe, pReq, dwReqSize, pvResponse, dwResponseSize, &dwBytesRead, &olp)) {
        dwLastErr = GetLastError();

        if (dwLastErr == ERROR_MORE_DATA) {
            // ...
        }
        else if (dwLastErr == ERROR_IO_PENDING) {
            if(WaitForSingleObject(hEvent, dwTimeout) == WAIT_OBJECT_0)
                lRet = olp.InternalHigh;
            else
                lRet = IPC_TIMEOUT; // timeout
        }
    }

    if(lRet <= 0)
        TraceMsg(TEXT("SendIPCMessage Error: %d.\n"), lRet);

    SAFE_FREE(pReq);
    SAFE_CLOSE(hEvent);
    SAFE_CLOSE(hPipe);

    return lRet;
}

BOOL PostIPCMessage(LPCTSTR szIPCName, LPVOID pvMsg, DWORD dwMsgSize) {
    if(!pvMsg)
        return FALSE;

    TCHAR szPipeName[MAX_PATH + 1] = {0};
    _tcscpy_s(szPipeName, MAX_PATH, TEXT("\\\\.\\pipe\\"));
    _tcscat_s(szPipeName, MAX_PATH, szIPCName);

    HANDLE hPipe = INVALID_HANDLE_VALUE;

    do {
        if ((hPipe = CreateFile(szPipeName,
                                GENERIC_READ |
                                GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                FILE_FLAG_OVERLAPPED,
                                NULL)) == INVALID_HANDLE_VALUE) {
            if (GetLastError() == ERROR_PIPE_BUSY) {
                if (WaitNamedPipe(szPipeName, 2000))
                    continue;
            }
        }

        break;
    }
    while (true);

    if(hPipe == INVALID_HANDLE_VALUE)
        return FALSE;

    DWORD dwMode = PIPE_READMODE_MESSAGE;

    if (!SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL)) {
        CloseHandle(hPipe);
        return FALSE;
    }

    OVERLAPPED olp;
    memset(&olp, 0, sizeof(olp));

    MSGREQ_HEADER reqHeader;
    reqHeader.bNeedAnswer = FALSE;
    reqHeader.dwHeaderSize = sizeof(MSGREQ_HEADER);
    reqHeader.dwContentSize = dwMsgSize;

    DWORD dwReqSize = reqHeader.dwHeaderSize + reqHeader.dwContentSize;
    char *pReq = (char *)malloc(dwReqSize);

    if(!pReq)
        return FALSE;

    memcpy(pReq, &reqHeader, sizeof(MSGREQ_HEADER));
    memcpy((pReq + sizeof(MSGREQ_HEADER)), pvMsg, dwMsgSize);

    BOOL bRet = TRUE;
    DWORD dwBytesWritten = 0;

    if(!(bRet = WriteFile(hPipe, pReq, dwReqSize, &dwBytesWritten, &olp))) {
        DWORD dwGLE = GetLastError();

        if(dwGLE != ERROR_IO_PENDING) {
            bRet = FALSE;
        }
    }


    SAFE_FREE(pReq);
    SAFE_CLOSE(hPipe);

    return bRet;
}